generalize callbacks for xmlgeneric.
use newly available pointer to data member callbacks to restore
yahoo functionality.
add a yahoo test.
.
add xml_init method to automatically generate Functors.
--- /dev/null
+No,Latitude,Longitude,Name\r
+1,37.416384,-122.024853,"701 FIRST AVE, SUNNYVALE, CA, 94089-1019, US"\r
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+
+<ResultSet xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+xmlns="urn:yahoo:maps"
+xsi:schemaLocation="urn:yahoo:maps http://local.yahooapis.com/MapsService/V1/GeocodeResponse.xsd">
+ <Result precision="address">
+<Latitude>37.416384</Latitude>
+
+<Longitude>-122.024853</Longitude>
+<Address>701 FIRST AVE</Address>
+<City>SUNNYVALE</City>
+<State>CA</State>
+
+<Zip>94089-1019</Zip>
+<Country>US</Country>
+ </Result>
+</ResultSet>
--- /dev/null
+
+gpsbabel -i yahoo -f ${REFERENCE}/yahoo.xml -o unicsv -F ${TMPDIR}/yahoo.csv
+compare ${REFERENCE}/yahoo.csv ${TMPDIR}/yahoo.csv
+
#include <QtCore/QHash> // for QHash
#include <QtCore/QIODevice> // for QIODevice, QIODevice::ReadOnly
#include <QtCore/QLatin1Char> // for QLatin1Char
+#include <QtCore/QList>
#include <QtCore/QStringRef> // for QStringRef
#include <QtCore/QTextCodec> // for QTextCodec
#include <QtCore/QXmlStreamAttributes> // for QXmlStreamAttributes
xg_shortcut_ignore
};
-static xg_tag_mapping* xg_tag_tbl;
+static QList<xg_tag_map_entry>* xg_tag_tbl;
+static bool dynamic_tag_tbl;
static QHash<QString, xg_shortcut>* xg_shortcut_taglist;
static QString rd_fname;
* xml strains and insulates us from a lot of the grubbiness of expat.
*/
-xg_callback*
+XgCallbackBase*
xml_tbl_lookup(const QString& tag, xg_cb_type cb_type)
{
const QByteArray key = tag.toUtf8();
const char* keyptr = key.constData();
- for (xg_tag_mapping* tm = xg_tag_tbl; tm->tag_cb != nullptr; ++tm) {
- if ((cb_type == tm->cb_type) && str_match(keyptr, tm->tag_name)) {
- return tm->tag_cb;
+ for (const auto& tm : qAsConst(*xg_tag_tbl)) {
+ if ((cb_type == tm.cb_type) && str_match(keyptr, tm.tag_name)) {
+ return tm.tag_cb;
}
}
return nullptr;
}
void
-xml_init(const QString& fname, xg_tag_mapping* tbl, const char* encoding,
+xml_common_init(const QString& fname, const char* encoding,
const char** ignorelist, const char** skiplist)
{
rd_fname = fname;
- xg_tag_tbl = tbl;
+
xg_encoding = encoding;
if (encoding) {
QTextCodec* tcodec = QTextCodec::codecForName(encoding);
codec = tcodec;
}
}
+
xg_shortcut_taglist = new QHash<QString, xg_shortcut>;
if (ignorelist != nullptr) {
for (; ignorelist && *ignorelist; ++ignorelist) {
}
}
+void
+xml_init(const QString& fname, QList<xg_tag_map_entry>* tbl, const char* encoding,
+ const char** ignorelist, const char** skiplist, bool dynamic_tbl)
+{
+ xg_tag_tbl = tbl;
+ dynamic_tag_tbl = dynamic_tbl;
+
+ xml_common_init(fname, encoding, ignorelist, skiplist);
+}
+
+void
+xml_init(const QString& fname, xg_tag_mapping* tbl, const char* encoding,
+ const char** ignorelist, const char** skiplist)
+{
+ xg_tag_tbl = new QList<xg_tag_map_entry>;
+ dynamic_tag_tbl = true;
+ for (xg_tag_mapping* tm = tbl; tm->tag_cb != nullptr; ++tm) {
+ auto* cb = new XgFunctionPtrCallback(tm->tag_cb);
+ xg_tag_tbl->append({cb, tm->cb_type, tm->tag_name});
+ }
+
+ xml_common_init(fname, encoding, ignorelist, skiplist);
+}
+
void
xml_deinit()
{
+ if (dynamic_tag_tbl) {
+ for (const auto& tm : qAsConst(*xg_tag_tbl)) {
+ delete tm.tag_cb;
+ }
+ delete xg_tag_tbl;
+ }
+ xg_tag_tbl = nullptr;
+
reader_data.clear();
rd_fname.clear();
- xg_tag_tbl = nullptr;
+
xg_encoding = nullptr;
codec = utf8_codec;
+
delete xg_shortcut_taglist;
xg_shortcut_taglist = nullptr;
}
static void
xml_run_parser(QXmlStreamReader& reader)
{
- xg_callback* cb;
+ XgCallbackBase* cb;
QString current_tag;
while (!reader.atEnd()) {
cb = xml_tbl_lookup(current_tag, cb_start);
if (cb) {
const QXmlStreamAttributes attrs = reader.attributes();
- cb(nullptr, &attrs);
+ (*cb)(nullptr, &attrs);
}
cb = xml_tbl_lookup(current_tag, cb_cdata);
// thus we will not process the EndElement case as we will issue a readNext first.
// does a caller ever expect to be able to use both a cb_cdata and a
// cb_end callback?
- cb(c, nullptr);
+ (*cb)(c, nullptr);
current_tag.chop(reader.qualifiedName().length() + 1);
}
break;
cb = xml_tbl_lookup(current_tag, cb_end);
if (cb) {
- cb(reader.name().toString(), nullptr);
+ (*cb)(reader.name().toString(), nullptr);
}
current_tag.chop(reader.qualifiedName().length() + 1);
break;
// be convenient to overload some day.
using xg_string = const QString&;
-
enum xg_cb_type {
cb_start = 1,
cb_cdata,
cb_end,
};
-using xg_callback = void (xg_string, const QXmlStreamAttributes*);
+class XgCallbackBase
+{
+public:
+ XgCallbackBase() = default;
+ virtual ~XgCallbackBase() = default;
+ XgCallbackBase(const XgCallbackBase&) = delete;
+ XgCallbackBase& operator=(const XgCallbackBase&) = delete;
+ XgCallbackBase(XgCallbackBase&&) = delete;
+ XgCallbackBase& operator=(XgCallbackBase&&) = delete;
+
+ virtual void operator()(xg_string string, const QXmlStreamAttributes* attrs) const = 0;
+};
+
+template<class XgFormat>
+class XgFunctor : public XgCallbackBase
+{
+public:
+ using XgCb = void (XgFormat::*)(xg_string, const QXmlStreamAttributes*);
+ XgFunctor(XgFormat* obj, XgCb cb) : that_(obj), cb_(cb) {}
+ void operator()(xg_string string, const QXmlStreamAttributes* attrs) const override
+ {
+ (that_->*cb_)(string, attrs);
+ }
+private:
+ XgFormat* that_;
+ XgCb cb_;
+};
+
+class XgFunctionPtrCallback : public XgCallbackBase
+{
+public:
+ using XgCb = void (xg_string, const QXmlStreamAttributes*);
+ explicit XgFunctionPtrCallback(XgCb cb) : cb_(cb) {}
+ void operator()(xg_string string, const QXmlStreamAttributes* attrs) const override
+ {
+ (*cb_)(string, attrs);
+ }
+
+private:
+ XgCb* cb_;
+};
+
+// xml processing uses a QList<xg_tag_map_entry>.
+// You may generated this yourself. See method 1 below.
+// Or it may be generated for you using one of the subsequent
+// methods.
+struct xg_tag_map_entry {
+ XgCallbackBase* tag_cb;
+ xg_cb_type cb_type;
+ const char* tag_name;
+};
+
+// Table generation from an array containing function pointers.
+// The above table can be generated by xml_init. See method 2 below.
+// This is how things done historically before the Format class was
+// introduced.
+using xg_callback = void (xg_string, const QXmlStreamAttributes*);
struct xg_tag_mapping {
xg_callback* tag_cb;
xg_cb_type cb_type;
const char* tag_name;
};
-extern const char* xhtml_entities;
+// Table generation from a list containing member function pointers.
+// The above table can be generated by xml_init. See method 3 below.
+template<class MyFormat>
+struct xg_functor_map_entry {
+ using XgCb = void (MyFormat::*)(xg_string, const QXmlStreamAttributes*);
+ XgCb tag_cb;
+ xg_cb_type cb_type;
+ const char* tag_name;
+};
+
+template<class MyFormat, typename my_functor_map_entry>
+QList<xg_tag_map_entry>* build_xg_tag_map(MyFormat* instance, const QList<my_functor_map_entry>& map)
+{
+ auto* tag_tbl = new QList<xg_tag_map_entry>;
+ for (const auto& entry : qAsConst(map)) {
+ auto* tag_cb = new XgFunctor<MyFormat>(instance, entry.tag_cb);
+ tag_tbl->append({tag_cb, entry.cb_type, entry.tag_name});
+ }
+ return tag_tbl;
+}
+/*
+ * There are multiple ways to initialize with xml_init.
+ *
+ * 1. Build your own QList<xg_tag_map_entry>, and pass it.
+ * You own the table, you must do any required clean up.
+ * Your callbacks may be a mix of function pointers wrapped in XgFunctors
+ * and non-static member functions wrapped in XgFunctionPtrCallbacks.
+ * and XgFunctionPtrCallback(for static member functions or global functions) entries.
+ * xml_init(fname, tbl, encoding, ignorelist, skiplist, false);
+ * You must set the dynamic_tbl parameter to false so xml_deninit doesn't
+ * attempt to free the table resources when xml_deinit is called.
+ *
+ * 2. Have xml_init build and own a table of XgFunctionPtrCallback entries
+ * from an array of function pointers, i.e. a xg_tag_mapping array.
+ * This only works when all callbacks are function pointers.
+ * xml_init(fname, tbl, encoding, ignorelist, skiplist);
+ * Generated table entries will automatically be freed.
+ *
+ * 3. Have xml_init build and own a table of XgFunctor entries from a list
+ * of non-static member functions, i.e. a QList<my_functor_map_entry>.
+ * This only works when all callbacks are non-static member functions.
+ * xml_init(fname, build_xg_tag_map(instance, map), encoding, ignorelist, skiplist, true);
+ * You must set the dynamic_tbl parameter to true to free the generated table
+ * resources when xml_deinit is called.
+ *
+ */
+void xml_init(const QString& fname, QList<xg_tag_map_entry>* tbl, const char* encoding,
+ const char** ignorelist = nullptr,
+ const char** skiplist = nullptr, bool dynamic_tbl = false);
void xml_init(const QString& fname, xg_tag_mapping* tbl,const char* encoding,
const char** ignorelist = nullptr,
const char** skiplist = nullptr);
*/
+#include <QtCore/QXmlStreamAttributes> // for QXmlStreamAttributes
+
#include "defs.h"
#include "yahoo.h"
-#include "xmlgeneric.h"
-#include <QtCore/QXmlStreamAttributes>
+#include "xmlgeneric.h" // for xg_string, build_xg_tag_map, xml_deinit, xml_init, xml_read
#define MYNAME "yahoo"
-// static xg_callback wpt_s, wpt_lat, wpt_lon, wpt_e;
-// static xg_callback wpt_addr /*, wpt_city, wpt_state, wpt_zip, wpt_country*/;
-#if 0
-static xg_tag_mapping gl_map[] = {
- { wpt_s, cb_start, "/ResultSet/Result" },
- { wpt_lat, cb_cdata, "/ResultSet/Result/Latitude" },
- { wpt_lon, cb_cdata, "/ResultSet/Result/Longitude" },
- { wpt_addr, cb_cdata, "/ResultSet/Result/Address" },
- { wpt_addr, cb_cdata, "/ResultSet/Result/City" },
- { wpt_addr, cb_cdata, "/ResultSet/Result/State" },
- { wpt_addr, cb_cdata, "/ResultSet/Result/Zip" },
- { wpt_addr, cb_cdata, "/ResultSet/Result/Country" },
- { wpt_e, cb_end, "/ResultSet/Result" },
- { nullptr, (xg_cb_type)0, nullptr}
-};
-#endif
-
void
YahooFormat::rd_init(const QString& fname)
{
-abort();
-// xml_init(fname, gl_map, nullptr);
+ xml_init(fname, build_xg_tag_map(this, gl_map), nullptr, nullptr, nullptr, true);
}
void
}
wpt_tmp->notes += args;
}
-#if 0
-ff_vecs_t yahoo_vecs = {
- ff_type_file,
- { ff_cap_read, ff_cap_none, ff_cap_none },
- yahoo_rd_init,
- nullptr,
- yahoo_rd_deinit,
- nullptr,
- yahoo_read,
- nullptr,
- nullptr,
- &yahoo_args,
- CET_CHARSET_ASCII, 0 /* CET-REVIEW */
- , NULL_POS_OPS,
- nullptr
-};
-#endif
#ifndef YAHOO_H_INCLUDED_
#define YAHOO_H_INCLUDED_
+#include <QtCore/QList> // for QList
#include <QtCore/QString> // for QString
-#include <QtCore/QStringList> // for QStringList
-#include <QtCore/QXmlStreamAttributes>
+#include <QtCore/QVector> // for QVector
+#include <QtCore/QXmlStreamAttributes> // for QXmlStreamAttributes
#include "defs.h"
#include "format.h" // for Format
-#include "xmlgeneric.h" // for Format
+#include "xmlgeneric.h" // for xg_tag_map_entry, cb_cdata, XgFunctor, cb_end, cb_start
class YahooFormat : public Format
{
return ff_type_file;
}
- QVector<ff_cap> get_cap() const override {
+ QVector<ff_cap> get_cap() const override
+ {
return {
ff_cap_read, // waypoints
ff_cap_none, // tracks
};
}
- QString get_encode() const override {
+ QString get_encode() const override
+ {
return CET_CHARSET_ASCII;
}
- int get_fixed_encode() const override {
+ int get_fixed_encode() const override
+ {
return 0;
}
void rd_deinit() override;
private:
- Waypoint* wpt_tmp;
- char* as;
-
- QVector<arglist_t> yahoo_args = {
- {
- "addrsep", &as,
- "String to separate concatenated address fields (default=\", \")",
- ", ", ARGTYPE_STRING, ARG_NOMINMAX, nullptr
- },
- };
- void wpt_s(const QString &, const QXmlStreamAttributes *);
-
- void wpt_e(const QString &, const QXmlStreamAttributes *);
- void wpt_lat(const QString &, const QXmlStreamAttributes *);
- void wpt_lon(const QString &, const QXmlStreamAttributes *);
- void wpt_addr(const QString &, const QXmlStreamAttributes *);
-// xg_tag_mapping gl_map[];
-
+ Waypoint* wpt_tmp;
+ char* as;
+
+ QVector<arglist_t> yahoo_args = {
+ {
+ "addrsep", &as,
+ "String to separate concatenated address fields (default=\", \")",
+ ", ", ARGTYPE_STRING, ARG_NOMINMAX, nullptr
+ },
+ };
+
+ void wpt_s(const QString&, const QXmlStreamAttributes*);
+ void wpt_e(const QString&, const QXmlStreamAttributes*);
+ void wpt_lat(const QString&, const QXmlStreamAttributes*);
+ void wpt_lon(const QString&, const QXmlStreamAttributes*);
+ void wpt_addr(const QString&, const QXmlStreamAttributes*);
+
+ QList<xg_functor_map_entry<YahooFormat>> gl_map = {
+ {&YahooFormat::wpt_s, cb_start, "/ResultSet/Result"},
+ {&YahooFormat::wpt_lat, cb_cdata, "/ResultSet/Result/Latitude"},
+ {&YahooFormat::wpt_lon, cb_cdata, "/ResultSet/Result/Longitude"},
+ {&YahooFormat::wpt_addr, cb_cdata, "/ResultSet/Result/Address"},
+ {&YahooFormat::wpt_addr, cb_cdata, "/ResultSet/Result/City"},
+ {&YahooFormat::wpt_addr, cb_cdata, "/ResultSet/Result/State"},
+ {&YahooFormat::wpt_addr, cb_cdata, "/ResultSet/Result/Zip"},
+ {&YahooFormat::wpt_addr, cb_cdata, "/ResultSet/Result/Country"},
+ {&YahooFormat::wpt_e, cb_end, "/ResultSet/Result"}
+ };
};
#endif // YAHOO_H_INCLUDED_